#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include "sboxes.h"

//0x748502cd  0x38451097
//0x1a624c89  0x520dec46

char *
int2bits (int i)
{
  switch (i)
    {
    case 0:
      return "0000";
    case 1:
      return "0001";
    case 2:
      return "0010";
    case 3:
      return "0011";
    case 4:
      return "0100";
    case 5:
      return "0101";
    case 6:
      return "0110";
    case 7:
      return "0111";
    case 8:
      return "1000";
    case 9:
      return "1001";
    case 10:
      return "1010";
    case 11:
      return "1011";
    case 12:
      return "1100";
    case 13:
      return "1101";
    case 14:
      return "1110";
    case 15:
      return "1111";
    default:
      return NULL;
    }
  return NULL;
}

int
getRow (char *c, int index)
{				// 0 <= index < 8
  char p, d;			// first & last bit
  p = c[index];
  d = c[index + 5];
  return 2 * (p - '0') + (d - '0');
}

int
getColumn (char *c, int index)
{
  char b1, b2, b3, b4;
  b1 = c[index + 1];
  b2 = c[index + 2];
  b3 = c[index + 3];
  b4 = c[index + 4];
  return 8 * (b1 - '0') + 4 * (b2 - '0') + 2 * (b3 - '0') + 1 * (b4 - '0');
}

int
bin2int (char *filename)
{
  int res;
  char *tmp;
  tmp = (char *) malloc (6 * sizeof (*tmp));
  strncpy (tmp, filename + strlen (filename) - 10, 6);

  res =
    32 * (tmp[0] - '0') + 16 * (tmp[1] - '0') + 8 * (tmp[2] - '0') +
    4 * (tmp[3] - '0') + 2 * (tmp[4] - '0') + 1 * (tmp[5] - '0');

  free (tmp);

  return res;
}


int
main (int argc, char **argv)
{
  FILE *key;
  FILE *msg;
  char c;
  char d;
  char e;
  char f;
  char tmp;
  char k[64] = { 0 };
  char m[64] = { 0 };
  //char m2[64];
  char L[32];
  char R[32];
  char RE[48];			// E(R), 48 bits
  char KP[64 - 8];
  char K1[16][48];		// subkeys
  char ExorK[48];		// ExorK = E^K, 48 bits
  char res[33];			// 32bits
  char P[33];
  //char LR[64];                        //pre output
  //char cipher[64];            //output
  char filename_key[64];
  char filename_msg[64];
  int i;
  int j;
  int z;
  int l;
  int rd;
  int index_k, index_m;
  int n0, n1;
  int round;
  int scanfr;			// return value of a fscanf

  if (argc <= 2)
    {
      printf ("usage:\t./des <nbrondes> -k [<file.key>]+ -m [<file.msg>]+\n");
      return 0;
    }

  //sbox=atoi(argv[1]);
  round = atoi (argv[1]);
  index_k = 0;
  index_m = 0;

  for (i = 2; i < argc; ++i)
    {				// we want key = argv[index_k], msg = argv[index_m]
      if (strcmp (argv[i], "-k") == 0)
	{
	  index_k = i + 1;
	}
      else if (strcmp (argv[i], "-m") == 0)
	{
	  index_m = i + 1;
	}
    }


  n0 = 0;
  n1 = 0;
  // We may get filenames with spaces!
  for (z = index_k; z <= index_m - 2; ++z)
    {				// k          
      //strcpy(filename_key, argv[z]);

      for (l = index_m; l < argc; ++l)
	{			// m

	  strcpy (filename_key, argv[z]);
	  strcpy (filename_msg, argv[l]);



//printf("filename_key=%s\n", filename_key);
//printf("filename_msg=%s\n", filename_msg);    

	  msg = fopen (filename_msg, "r");
	  if (msg == NULL)
	    {
	      printf ("Probleme dans l'ouverture du fichier %s\n",
		      filename_msg);
	      return 0;
	    }

	  scanfr = fscanf (msg, "%s\n", m);
	  //printf("scanfr: %i\n", scanfr);

	  if (scanfr)
	    fclose (msg);
	  else
	    {
	      printf ("Error while scanning msg\n");
	      return 0;
	    }

//printf("m:\t");
//for(i=0;i<64;++i) printf("%c", m[i]);
//printf("\n");                 


	  // get the key
	  key = fopen (filename_key, "r");
	  if (key == NULL)
	    {
	      printf ("Probleme dans l'ouverture du fichier %s\n",
		      filename_key);
	      return 0;
	    }

	  if (fscanf (key, "%s\n", k))
	    fclose (key);
	  else
	    {
	      printf ("Error while scanning key\n");
	      return 0;
	    }


	  strncpy (L, m, 32);
	  strncpy (R, m + 32, 32);



	  // Key schedule
	  for (i = 1, j = 0; i <= 64; ++i)
	    {
	      if (i % 8 != 0)
		{
		  KP[j] = k[PC1[i] - 1];
		  j++;
		}
	    }

	  for (rd = 0; rd < 16; rd++)
	    {


	      if ((rd == 0) || (rd == 1) || (rd == 8) || (rd == 15))
		{
		  c = KP[0];
		  d = KP[28];
		  for (i = 0; i <= 54; ++i)
		    KP[i] = KP[i + 1];
		  KP[55] = d;
		  KP[27] = c;
		}
	      else
		{
		  c = KP[0];
		  d = KP[1];
		  e = KP[28];
		  f = KP[29];
		  for (i = 0; i <= 54; ++i)
		    KP[i] = KP[i + 2];
		  KP[54] = e;
		  KP[55] = f;
		  KP[26] = c;
		  KP[27] = d;
		}

	      for (i = 0; i < 48; ++i)
		K1[rd][i] = KP[PC2[i + 1] - 1];

	      // End KeySchedule!
//printf("K1[%d]:\t",rd+1);
//for(i=0;i<48;++i) printf("%c", K1[rd][i]);
//printf("\n");
	    }




// rondes
	  for (rd = 0; rd < round; rd++)
	    {
	      // Key Expansion
	      for (i = 0; i < 48; ++i)
		RE[i] = R[Expand[i + 1] - 1];

//printf("RE:\t");
//for(i=0;i<48;++i) printf("%c", RE[i]);
//printf("\n");

	      //for(i=1;i<=64;++i) m[i]=m2[IP[i]];
	      //for(i=0;i<48;++i) ExorK[i]='0'+(RE[i]^k[i]);


//printf("k:\t");
//for(i=0;i<64;++i) printf("%c", k[i]);
//printf("\n");


	      // XOR with Subkey      
	      for (i = 0; i < 48; ++i)
		ExorK[i] = '0' + (K1[rd][i] ^ RE[i]);

//printf("ExorK:\t");
//for(i=0;i<48;++i) printf("%c", ExorK[i]);
//printf("\n");
	      // Passing in S-Boxes   
	      for (i = 0; i < 8; ++i)
		{
		  strncpy (res + 4 * i,
			   int2bits (S[i][getRow (ExorK, 6 * i)]
				     [getColumn (ExorK, 6 * i)]), 4);
		}
	      res[32] = '\0';

//printf("res:\t%s\n", res);
//printf("P:\t");
	      for (i = 0; i < 32; i++)
		{
		  P[i] = res[Per[i] - 1];
		  //printf("%c", P[i]);
		}
	      P[32] = 0;

	      //writing of blocs L and R
	      for (i = 0; i < 32; ++i)
		{
		  tmp = L[i];
		  L[i] = R[i];
		  R[i] = '0' + (tmp ^ P[i]);
		}
//printf("L:\t");
//for(i=0;i<32;++i) printf("%c", L[i]);
//printf("\n");

//printf("R:\t");
//for(i=0;i<32;++i) printf("%c", R[i]);
//printf("\n");

	    }			//end round


	  if (round)
	    {
//printf("\nLR:\t");
//for(i=0;i<64;++i) printf("%c",LR[i]);
//printf("\n");


//printf("cipher:\t");
	      for (i = 0; i < 32; ++i)
		printf ("%c", L[i]);
	      printf ("\n");
	      printf ("\n");
	      for (i = 0; i < 32; ++i)
		printf ("%c", R[i]);
	      printf ("\n");
	    }

	  //for(i=index_m;i<argc;++i)
	  //for(i=index_k;i<=index_m-2;++i)
	}
    }

  return 0;
}
